Skip to content

docs(auth): align front-door flows with shipped workflows#80

Open
ndycode wants to merge 15 commits intogit-plan/05-settings-productizationfrom
git-plan/06-docs-frontdoor-cleanup
Open

docs(auth): align front-door flows with shipped workflows#80
ndycode wants to merge 15 commits intogit-plan/05-settings-productizationfrom
git-plan/06-docs-frontdoor-cleanup

Conversation

@ndycode
Copy link
Owner

@ndycode ndycode commented Mar 12, 2026

Summary

  • Align README, docs portal, getting-started, troubleshooting, and reference docs with the shipped workflows.
  • Make backup restore, startup recovery, sync center, and settings productization discoverable from the front door.
  • Dependency note: this branch should land after PR3 and PR5 because it documents both lines.

Summary by cubic

Aligns docs and the codex auth login front door with shipped workflows. Adds a startup restore prompt, TTY-only interactivity, and Windows-safe fallthrough to keep login smooth.

  • New Features

    • Startup recovery: when named backups exist and no accounts, prompt to “Restore From Backup” before OAuth (TTY-only; suppressed after reset/fresh).
    • CLI: isInteractiveLoginMenuAvailable() gates interactivity; non‑TTY uses the minimal flow.
    • Storage: getActionableNamedBackupRestores() lists recoverable backups via Promise.allSettled.
    • Docs: refresh README and docs for restore, sync center, and the settings split; clarified interactive prompt behavior and troubleshooting.
  • Bug Fixes

    • Login: swallow EPERM/EBUSY/EACCES and wrap assessment in try‑catch so errors skip the prompt and fall through to OAuth.
    • Sanitization: strip ANSI/control characters from backup names shown in prompts.
    • Sync center: wrap saveAccounts with withQueuedRetry(getStoragePath()) to avoid Windows write‑lock failures.
    • Tests: add coverage for access‑denied and write‑lock cases, sync‑center apply, and restore regressions; fix missing confirmMock for the restore manager path.

Written for commit 477a2fb. Summary will update on new commits.

note: greptile review for oc-chatgpt-multi-auth. cite files like lib/foo.ts:123. confirm regression tests + windows concurrency/token redaction coverage.

Greptile Summary

this pr aligns docs and the codex auth login front door with two shipped features — startup backup recovery and the productized settings split — and adds the code that makes those features discoverable. the code changes are focused: a new isInteractiveLoginMenuAvailable() gate, a startup recovery prompt in runAuthLogin, getActionableNamedBackupRestores in storage using Promise.allSettled, ANSI sanitization of backup names, and withQueuedRetry wrapping saveAccounts in the sync-center apply path.

key observations:

  • windows concurrency: the withQueuedRetry fix in settings-hub.ts is correct for write-lock races; the EPERM/EBUSY/EACCES catch in runAuthLogin falls through to oauth as intended, but recoveryPromptAttempted = true is committed before the catch, so a transient av lock permanently and silently suppresses the recovery prompt for the session (only a console.debug fires — not user-visible)
  • token safety / redaction: no token fields touched; no new logging of credentials observed
  • missing vitest coverage: codex-manager-cli.test.ts covers EBUSY and EPERM throws from getActionableNamedBackupRestoresMock but not EACCES — the EACCES test exists only at the storage layer (recovery.test.ts) via Promise.allSettled rejection, not at the runAuthLogin integration level
  • ansi sanitization: ANSI_CONTROL_RE strips SGR sequences and all C0/DEL chars from the backup name, but backupDir (derived from CODEX_MULTI_AUTH_DIR) is embedded unsanitized in the confirm prompt — a minor inconsistency given the stated injection-prevention goal
  • docs are accurate and match the shipped behavior

Confidence Score: 3/5

  • mergeable after addressing the transient-lock suppression behavior and the missing EACCES integration test
  • the docs and most code are correct; the withQueuedRetry and Promise.allSettled patterns are solid. the main blocker is recoveryPromptAttempted = true being set before the windows lock catch — a transient AV file lock silently suppresses recovery for the whole session with no user-visible signal, which is a real UX regression on windows desktops. the missing EACCES integration test in codex-manager-cli.test.ts is a secondary gap given the explicit per-PR rule requiring coverage for all three filesystem error codes
  • lib/codex-manager.tsrecoveryPromptAttempted placement relative to the filesystem lock catch block; test/codex-manager-cli.test.ts — missing EACCES mock-rejection test for getActionableNamedBackupRestoresMock

Important Files Changed

Filename Overview
lib/codex-manager.ts adds startup recovery prompt with ANSI sanitization and Windows lock fallthrough; recoveryPromptAttempted flag is set before the filesystem error catch, so a transient lock permanently silences the prompt; backupDir unsanitized in confirm message despite explicit sanitization of the backup name
lib/storage.ts adds getActionableNamedBackupRestores using Promise.allSettled for resilient per-backup assessment; correctly filters by valid/non-exceeding/positive-import; ActionableNamedBackupRecoveries interface is clean and testable
lib/cli.ts adds isInteractiveLoginMenuAvailable() as a tighter TTY gate that composes isNonInteractiveMode() and isTTY(); correctly gates the recovery prompt and replaces the previous bare isTTY() check
lib/codex-manager/settings-hub.ts wraps saveAccounts in withQueuedRetry(getStoragePath(), ...) in the sync-center apply path; directly addresses Windows write-lock race on the accounts file during sync
test/codex-manager-cli.test.ts solid new coverage for startup restore, EBUSY/EPERM fallthrough, suppress-after-reset, non-TTY skip, and sync-center EBUSY retry; dedicated EACCES integration test for the runAuthLogin catch path is missing (only covered at the storage layer in recovery.test.ts)
test/recovery.test.ts mostly indentation reformatting (2-space to tabs) plus new getActionableNamedBackupRestores describe block; EACCES test at storage level via Promise.allSettled rejection is present and correct
README.md accurate doc update: restore, sync center, and settings split added to feature list, quick example, workflows, and troubleshooting sections; no factual gaps spotted
docs/troubleshooting.md new restore and sync problem tables are accurate and actionable; clearly explains TTY requirement and intentional skip-after-reset behavior

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[runAuthLogin starts] --> B[setStoragePath null]
    B --> C[loadAccounts]
    C --> D{existingCount == 0 AND interactive TTY AND not suppressed AND not attempted?}
    D -- No --> K[run OAuth flow]
    D -- Yes --> E[recoveryPromptAttempted = true]
    E --> F[getActionableNamedBackupRestores]
    F -- Windows lock EPERM/EBUSY/EACCES --> G[console.debug, use empty state]
    F -- success --> H{assessments.length > 0?}
    G --> K
    H -- No --> K
    H -- Yes --> I[confirm: Restore now?]
    I -- declined --> K
    I -- confirmed --> J[runBackupRestoreManager]
    J --> C
    K[run OAuth flow] --> L{tokenResult type}
    L -- success --> M[persist account, sync to Codex]
    L -- cancelled with accounts --> C
    L -- cancelled no accounts --> N[return 0]
    L -- failed --> O[return 1]
    M --> P{add another?}
    P -- yes --> C
    P -- no --> N
    Q[mode fresh or reset] --> R[suppressRecoveryPrompt = true]
    R --> C
Loading

Comments Outside Diff (10)

  1. lib/codex-manager.ts, line 4078-4103 (link)

    startup recovery prompt - no windows concurrency reasoning or retry guard

    the runAuthLogin recovery prompt calls getActionableNamedBackupRestores, which in storage.ts fans out to Promise.all(backups.map(...assessNamedBackupRestore)). on windows, av tools can hold a read lock on .json files in ~/.codex/multi-auth/backups/. a concurrent read of multiple backup files will race those locks and can throw EBUSY or EPERM — silently swallowing the error and showing the user no restore prompt at all.

    per org rule, every code change must explain the windows filesystem concurrency defense and reference the regression test that reproduces the race. neither is present here.

    additionally, the backupLabel below leaks the raw backup file name (which could include account-derived path fragments) directly into the confirm() prompt string without scrubbing.

    Rule Used: What: Every code change must explain how it defend... (source)

  2. lib/codex-manager/settings-hub.ts, line 2949-2966 (link)

    sync center apply - error message may leak token data and saveAccounts is unguarded

    syncAccountStorageFromCodexCli can throw with an error whose .message comes from deep within token validation or oauth refresh. storing that raw message in statusDetail and rendering it in the sync center UI is a token leakage path — the same surface that rule 637a42e6 requires every patch to address.

    additionally, saveAccounts here is called without any windows write-queue guard. if an av tool holds an EBUSY lock on the storage file at the moment apply fires, the write silently fails and the ui continues as if nothing happened.

    Rule Used: What: Every code change must explain how it defend... (source)

  3. lib/storage.ts, line 1281-1311 (link)

    Promise.all over backup reads risks EBUSY on windows

    Promise.all(backups.map((backup) => assess(backup.name, ...))) fires all backup file reads simultaneously. on windows, av tools serialize directory scans and can hold shared read locks on each .json file in the backups directory. firing several concurrent fs.readFile calls into the same directory raises EBUSY or EPERM on the losing reads.

    the function has no retry shim and callers don't guard it. either serialize the reads sequentially or wrap each assess call with the same retry envelope used in withQueuedRetry for settings writes.

    no vitest race regression test is present for this path.

    Rule Used: What: Every code change must explain how it defend... (source)

  4. lib/codex-manager.ts, line 4078-4103 (link)

    Missing try-catch around backup assessment – login crash on Windows file lock

    getActionableNamedBackupRestores does a Promise.all over every backup file in ~/.codex/multi-auth/backups/. if any individual assessment throws (e.g. EPERM/EBUSY from antivirus holding a file on Windows), the entire awaited call rejects and bubbles up unhandled through runAuthLogin, crashing the login flow rather than falling through to OAuth.

    this is a high-probability Windows failure: the recovery prompt is gated on existingCount === 0, meaning it only fires on first-run or after a reset – exactly the moment a user is most likely to have a newly-placed backup file that an antivirus is still scanning.

    wrap the assessment call in a try-catch so that any filesystem error is swallowed and the prompt is simply skipped:

    let recoveryState: Awaited<ReturnType<typeof getActionableNamedBackupRestores>> | null = null;
    try {
        recoveryState = await getActionableNamedBackupRestores({
            currentStorage: refreshedStorage,
        });
    } catch {
        // filesystem error (e.g. Windows EPERM/EBUSY) – skip recovery prompt
        recoveryState = { assessments: [], totalBackups: 0 };
    }
    if (recoveryState.assessments.length > 0) {
        // ... existing prompt logic
    }
  5. lib/storage.ts, line 1298-1311 (link)

    Promise.all has no per-backup error isolation – one bad file blocks all

    Promise.all rejects on the first thrown assessment. on Windows, antivirus or another process can lock any individual backup file at any time. a single EBUSY/EPERM on one file fails the entire getActionableNamedBackupRestores call, making the result useless for the remaining valid backups.

    use Promise.allSettled and filter to fulfilled results only:

    const settled = await Promise.allSettled(
        backups.map((backup) => assess(backup.name, { currentStorage })),
    );
    const assessments = settled
        .filter((r): r is PromiseFulfilledResult<Awaited<ReturnType<typeof assessNamedBackupRestore>>> => r.status === "fulfilled")
        .map((r) => r.value);

    then apply the existing actionable filter on assessments. this way one unreadable file doesn't block recovery for the rest.

  6. lib/codex-cli/sync.ts, line 555-557 (link)

    lastCodexCliSyncRun module-level state is overwritten by concurrent async operations

    lastCodexCliSyncRun is a shared mutable module variable. if previewCodexCliSync and syncAccountStorageFromCodexCli are both awaited concurrently in the same process (possible if a background live-sync fires while the sync-center preview is refreshing), the last setLastCodexCliSyncRun call wins regardless of which operation actually finished last.

    the returned getLastCodexCliSyncRun() snapshot might then reflect a preview run's outcome when the UI was expecting to see the apply outcome, or vice versa.

    consider stamping each run with an incrementing sequence number and exposing both the latest preview run and the latest apply run separately, or at minimum document the last-write-wins semantic in the interface comment so callers know not to rely on ordering.

  7. lib/codex-manager/settings-hub.ts, line 2957-2960 (link)

    saveAccounts has no EBUSY/EPERM retry in the sync apply path

    saveAccounts(synced.storage) is called here with no Windows-style retry guard. compare to withQueuedRetry used for every settings write in this same file. on windows, antivirus or another process holding the storage file will throw EBUSY/EPERM, the outer catch swallows it into preview.status = "error", and the sync result is silently lost. the user has to refresh and apply again.

    the existing RETRYABLE_SETTINGS_WRITE_CODES set and withQueuedRetry already solve this pattern; the apply path should go through it or at least through the same enqueueSettingsWrite queue to avoid clobbering concurrent settings writes on the same path.

  8. lib/live-account-sync.ts, line 3246-3248 (link)

    Module-level singleton clobbered by multiple LiveAccountSync instances

    publishSnapshot() overwrites lastLiveAccountSyncSnapshot unconditionally. if more than one LiveAccountSync instance is alive simultaneously (plugin mode + a test helper, or a second call to syncToPath without calling stop first), the older instance's state is lost on every reload/stop/error event.

    the sync center reads getLastLiveAccountSyncSnapshot() to show live-watcher status; if a second instance is created and then stopped before the user opens the sync center, the display will show "idle" even though the original watcher is still running.

    consider storing snapshots keyed by path, or at minimum assert in syncToPath that this.running === false before overwriting the singleton.

  9. lib/codex-manager.ts, line 491-499 (link)

    EACCES missing from Windows filesystem fallthrough whitelist

    the catch block only swallows EPERM and EBUSY, but Windows also raises EACCES (access denied) when the backup directory has restrictive ACLs or is under a folder that antivirus has locked at the directory level. if that happens, getActionableNamedBackupRestores throws EACCES, the condition code !== "EPERM" && code !== "EBUSY" is true, and the error is re-thrown — crashing the login flow instead of silently falling through to OAuth.

    the regression test added for EPERM/EBUSY should be extended with an EACCES case to lock in this behavior.

  10. lib/codex-manager.ts, line 4383-4403 (link)

    Transient Windows lock permanently suppresses recovery prompt

    recoveryPromptAttempted = true is set on line 4383 before the try-catch. When a Windows filesystem lock (EBUSY/EACCES/EPERM) is caught, the flag is already true, so the recovery prompt is permanently suppressed for the rest of the session — even if the lock was transient (e.g., antivirus briefly holding a file). The only signal is a console.debug that users never see.

    A user who hits a momentary AV lock on first login would silently bypass recovery and go straight to OAuth, losing access to their named backups until they explicitly navigate to Restore From Backup.

    Consider moving recoveryPromptAttempted = true into the path where the assessment actually succeeds and a prompt is shown, or at minimum emit a visible warning (not just console.debug) when a filesystem lock causes a skip:

    if (canPromptForRecovery) {
        let recoveryState: ...;
        try {
            recoveryState = await getActionableNamedBackupRestores({ currentStorage: refreshedStorage });
            recoveryPromptAttempted = true; // only committed after a successful read
        } catch (error) {
            const code = error instanceof Error && "code" in error ? String(error.code) : undefined;
            if (code !== "EPERM" && code !== "EBUSY" && code !== "EACCES") {
                throw error;
            }
            // filesystem lock – skip this attempt but allow retry on next loop iteration
            console.warn(`[recovery-prompt] backup assessment skipped (${code}); will retry next iteration`);
            recoveryState = { assessments: [], totalBackups: 0 };
        }
        ...

Fix All in Codex

Prompt To Fix All With AI
This is a comment left during a code review.
Path: lib/codex-manager.ts
Line: 4383-4403

Comment:
**Transient Windows lock permanently suppresses recovery prompt**

`recoveryPromptAttempted = true` is set on line 4383 *before* the `try-catch`. When a Windows filesystem lock (EBUSY/EACCES/EPERM) is caught, the flag is already `true`, so the recovery prompt is permanently suppressed for the rest of the session — even if the lock was transient (e.g., antivirus briefly holding a file). The only signal is a `console.debug` that users never see.

A user who hits a momentary AV lock on first login would silently bypass recovery and go straight to OAuth, losing access to their named backups until they explicitly navigate to `Restore From Backup`.

Consider moving `recoveryPromptAttempted = true` into the path where the assessment actually succeeds and a prompt is shown, or at minimum emit a visible warning (not just `console.debug`) when a filesystem lock causes a skip:

```typescript
if (canPromptForRecovery) {
    let recoveryState: ...;
    try {
        recoveryState = await getActionableNamedBackupRestores({ currentStorage: refreshedStorage });
        recoveryPromptAttempted = true; // only committed after a successful read
    } catch (error) {
        const code = error instanceof Error && "code" in error ? String(error.code) : undefined;
        if (code !== "EPERM" && code !== "EBUSY" && code !== "EACCES") {
            throw error;
        }
        // filesystem lock – skip this attempt but allow retry on next loop iteration
        console.warn(`[recovery-prompt] backup assessment skipped (${code}); will retry next iteration`);
        recoveryState = { assessments: [], totalBackups: 0 };
    }
    ...
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: test/codex-manager-cli.test.ts
Line: 909-953

Comment:
**Missing EACCES integration test for `runAuthLogin` catch block**

The PR description says "add coverage for `EACCES`", and there is an EACCES test in `recovery.test.ts` — but that tests `Promise.allSettled` within `getActionableNamedBackupRestores`. There is no test in `codex-manager-cli.test.ts` that makes `getActionableNamedBackupRestoresMock` itself reject with `EACCES`, which is the code path guarded by the catch block in `runAuthLogin`.

EBUSY and EPERM are tested — EACCES should have the same treatment here for complete coverage of the Windows fallback condition, especially given the rule requiring regression tests for all three codes:

```typescript
it("falls through to OAuth when backup assessment throws EACCES", async () => {
    setInteractiveTTY(true);
    loadAccountsMock.mockResolvedValue({ version: 3, activeIndex: 0, activeIndexByFamily: { codex: 0 }, accounts: [] });
    const eacces = Object.assign(new Error("EACCES: permission denied"), { code: "EACCES" });
    getActionableNamedBackupRestoresMock.mockRejectedValueOnce(eacces);
    selectMock.mockResolvedValueOnce("cancel");

    const { runCodexMultiAuthCli } = await import("../lib/codex-manager.js");
    const exitCode = await runCodexMultiAuthCli(["auth", "login"]);

    expect(exitCode).toBe(0);
    expect(getActionableNamedBackupRestoresMock).toHaveBeenCalledTimes(1);
    expect(confirmMock).not.toHaveBeenCalled();
    expect(createAuthorizationFlowMock).toHaveBeenCalledTimes(1);
});
```

**Rule Used:** What: Every code change must explain how it defend... ([source](https://app.greptile.com/review/custom-context?memory=637a42e6-7a78-40d6-9ef8-6a45e02e73b6))

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: lib/codex-manager.ts
Line: 4419-4422

Comment:
**`backupDir` not sanitized in confirm prompt**

`backupLabel` (from backup filename) is sanitized via `ANSI_CONTROL_RE`, but `backupDir` (from `getNamedBackupsDirectoryPath()`, which derives from `CODEX_MULTI_AUTH_DIR` or the home dir path) is embedded in the prompt without sanitization. an attacker who controls the env var could inject ANSI control codes into the terminal via the directory portion of this prompt.

low risk in practice (requires local env access), but inconsistent given the explicit sanitization effort on the name side:

```typescript
const sanitizedDir = backupDir.replace(ANSI_CONTROL_RE, "").trim();
const restoreNow = await confirm(
    `Found ${recoveryState.assessments.length} recoverable backup${
        recoveryState.assessments.length === 1 ? "" : "s"
    } (${backupLabel}) in ${sanitizedDir}. Restore now?`,
);
```

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: 477a2fb

Context used:

  • Rule used - What: Every code change must explain how it defend... (source)

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 12, 2026

📝 Walkthrough

walkthrough

this pr adds interactive backup-restore and preview-first sync workflows, updates docs across the project, and exposes utilities to detect interactive login availability and to enumerate actionable named backups. the login flow now may prompt to restore before OAuth and suppresses prompts for explicit destructive actions (lib/codex-manager.ts:45, lib/cli.ts:12, lib/storage.ts:46).

changes

Cohort / File(s) Summary
documentation
README.md, docs/README.md, docs/index.md, docs/features.md, docs/getting-started.md, docs/reference/commands.md, docs/reference/settings.md, docs/troubleshooting.md
expanded user and reference docs to include backup restore workflows, startup recovery prompts, preview-first Codex CLI sync, settings split (everyday vs advanced/operator), and new interactive workflow docs.
interactive login & recovery flow
lib/codex-manager.ts
adds canPromptForRecovery logic, recovery prompt flow triggered before OAuth, recoveryPromptAttempted guard, and suppression on destructive actions. imports interactive guard and actionable backup helper.
cli & storage public surface
lib/cli.ts, lib/storage.ts
exports isInteractiveLoginMenuAvailable() and adds ActionableNamedBackupRecoveries + getActionableNamedBackupRestores() which assess and filter named backups for restoration readiness.
settings persistence tweak
lib/codex-manager/settings-hub.ts
replace direct save with path-aware queued retry persistence using getStoragePath for account sync saves.
tests: cli + recovery
test/codex-manager-cli.test.ts, test/recovery.test.ts
large mock refactor and new tests covering startup restore prompts, EBUSY handling during assessment, suppressed prompts after fresh/reset, and actionable backup restore flows. test/recovery.test.ts reorganized and reformatted.

sequence diagram(s)

sequenceDiagram
    participant user as User
    participant cli as CLI
    participant login as LoginFlow
    participant storage as BackupStorage
    participant assess as BackupAssessment
    participant oauth as OAuth

    user->>cli: codex auth login
    cli->>login: start interactive login
    login->>storage: getActionableNamedBackupRestores()
    storage->>assess: assess backups (concurrent)
    assess-->>storage: assessment results
    storage-->>login: {assessments, totalBackups}
    alt actionable backups exist
        login->>user: prompt "Restore From Backup"
        user->>login: choose restore or skip
        alt restore
            login->>storage: run selected restore
            storage-->>login: restore complete
        else skip
            login->>oauth: proceed to OAuth
        end
    else no actionable backups
        login->>oauth: proceed to OAuth
    end
    oauth-->>user: complete login
Loading

estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

suggested labels

bug

additional flags

  • missing regression test: no explicit test asserts windows path handling for backup locations (lib/storage.ts:46). add tests covering backslash separators and case differences for named backup discovery. cite failing test target: test/codex-manager-cli.test.ts:314.
  • windows edge case: verify backup directory traversal and path normalization on windows in lib/storage.ts:46 (regression risk when opening named backups).
  • concurrency risk: lib/codex-manager.ts:45 uses recoveryPromptAttempted guard; check race where two concurrent login flows may both call getActionableNamedBackupRestores() before the guard flips. add a test for parallel login attempts.
  • missing coverage: add an end-to-end test that asserts interactive prompt is suppressed when isInteractiveLoginMenuAvailable() is false (lib/cli.ts:12) and that non-interactive/fallback login paths never trigger recovery logic (test/codex-manager-cli.test.ts:314).
🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description is comprehensive with clear objectives, code changes, test additions, and documented dependencies. However, it lacks explicit validation checklist completion status and risk/rollback planning detail. Confirm which validation steps (npm run lint, typecheck, test, build) passed. Document risk level assessment and explicit rollback plan for the startup recovery prompt flow and Windows concurrency changes.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed Title follows conventional commits format (docs type, lowercase summary ≤72 chars) and clearly summarizes the main change: aligning docs with shipped workflows.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch git-plan/06-docs-frontdoor-cleanup
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 13 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="test/recovery.test.ts">

<violation number="1" location="test/recovery.test.ts:1224">
P2: This new test suite is skipped, so the added backup-restore assertions never run. Use an active `describe` (or add explicit justification if this must remain skipped).</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

ndycode added 2 commits March 15, 2026 18:17
Wrap getActionableNamedBackupRestores in try-catch so a filesystem
error (e.g. antivirus holding a backup file on Windows) skips the
recovery prompt instead of crashing the login flow.

Switch Promise.all to Promise.allSettled in getActionableNamedBackupRestores
so one unreadable backup file does not block assessment of the rest.
Verify that when getActionableNamedBackupRestores throws EBUSY the
login flow falls through to OAuth instead of crashing.
Copilot AI review requested due to automatic review settings March 15, 2026 10:18
…ion' into git-plan/06-docs-frontdoor-cleanup

# Conflicts:
#	test/codex-manager-cli.test.ts
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Aligns the “front door” docs and interactive codex auth login flows with shipped workflows by surfacing startup recovery/restore, Codex CLI sync center, and the new Everyday vs Advanced/Operator settings split.

Changes:

  • Adds a startup recovery prompt (TTY-only) that offers backup restore before OAuth when actionable named backups exist.
  • Introduces sync-center preview/run tracking and exposes watcher/sync snapshots for UI surfaces.
  • Refreshes README + docs to highlight restore, sync center, settings split, and related troubleshooting guidance.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
test/settings-hub-utils.test.ts Adds coverage for sync-center overview text and improves test formatting.
test/recovery.test.ts Reformats tests and adds coverage for actionable named backup restore filtering.
test/live-account-sync.test.ts Adds coverage for publishing watcher snapshot state used by sync-center/status UIs.
test/codex-manager-cli.test.ts Adds coverage for startup restore prompt, TTY gating, suppress-after-reset, settings split, and sync-center UX.
test/codex-cli-sync.test.ts Expands sync tests to cover preview summaries and last-run tracking.
lib/ui/copy.ts Productizes settings copy (Everyday vs Advanced & Operator) and adds sync-center UI strings.
lib/storage.ts Adds getActionableNamedBackupRestores() using Promise.allSettled to isolate per-backup read failures.
lib/live-account-sync.ts Publishes a module-level snapshot for sync-center/status surfaces and adds test reset helper.
lib/codex-manager.ts Adds startup recovery prompt path before OAuth, with suppression on reset/fresh actions and try/catch fallback.
lib/codex-cli/sync.ts Refactors reconciliation into helper, adds preview API, and tracks “last sync run” in module state.
lib/cli.ts Adds isInteractiveLoginMenuAvailable() gate and uses it for fallback login mode.
docs/troubleshooting.md Adds restore + sync troubleshooting sections and front-door guidance.
docs/reference/settings.md Documents Everyday vs Advanced & Operator settings and the sync-center workflow.
docs/reference/commands.md Updates codex auth login description and lists interactive workflow packs.
docs/index.md Updates landing page to mention restore and sync workflows.
docs/getting-started.md Adds startup restore prompt guidance + restore/start-fresh and sync/settings sections.
docs/features.md Updates capabilities list for startup prompt, restore manager, settings split, and sync center.
docs/README.md Updates docs portal index descriptions to match new workflows.
README.md Updates project overview and common workflows to surface restore + sync center.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +4392 to +4401
const backupLabel =
sample?.backup.name ??
`${recoveryState.assessments.length} backup${
recoveryState.assessments.length === 1 ? "" : "s"
}`;
const restoreNow = await confirm(
`Found ${recoveryState.assessments.length} recoverable backup${
recoveryState.assessments.length === 1 ? "" : "s"
} (${backupLabel}) in ${backupDir}. Restore now?`,
);
Comment on lines +1447 to +1453
const authModule = await import("../lib/auth/auth.js");
const createAuthorizationFlowMock = vi.mocked(
authModule.createAuthorizationFlow,
);
createAuthorizationFlowMock.mockRejectedValue(
new Error("oauth flow should be skipped when restoring backup"),
);
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/reference/commands.md`:
- Around line 99-101: Update the "Startup recovery prompt" entry to state that
the restore prompt appears only during interactive/TTY logins (i.e., when the
interactive login flow is used), matching the runtime guard in
lib/codex-manager.ts:4373; specifically note the tty/interactive requirement so
docs reflect shipped behavior and add an upgrade note if this is a behavior
change.

In `@lib/cli.ts`:
- Around line 29-31: The isInteractiveLoginMenuAvailable function redundantly
calls isTTY(); simplify it by relying solely on isNonInteractiveMode(): change
isInteractiveLoginMenuAvailable to return !isNonInteractiveMode() only. Update
the function (isInteractiveLoginMenuAvailable) and remove the extra isTTY()
dependency so the check is not duplicated (keep isNonInteractiveMode and isTTY
implementations unchanged).

In `@lib/codex-manager.ts`:
- Around line 4383-4386: The catch block in the backup assessment (in
lib/codex-manager.ts) currently swallows all errors; change it to catch the
thrown error (e.g., catch (err)) and only treat filesystem lock errors by
checking err.code === 'EPERM' || err.code === 'EBUSY' (or the
platform-equivalent) and set recoveryState = { assessments: [], totalBackups: 0
} for those cases; for any other error rethrow it so real failures surface. Also
update/add vitest tests referenced around test/codex-manager-cli.test.ts:570 to
assert that non-errno exceptions are not treated as the Windows lock fallback
(i.e., expect them to propagate or be handled differently) and include coverage
for EBUSY/EPERM paths.

In `@lib/storage.ts`:
- Around line 1340-1343: The check using options.currentStorage === undefined
distinguishes "option omitted" from an explicit null value; add an inline
comment next to the assignment of currentStorage (the ternary using
options.currentStorage and loadAccounts()) explaining that undefined means
"auto-load via loadAccounts()" while null is a valid explicit value meaning "no
current storage", so future maintainers understand the intentional undefined vs
null semantics for options.currentStorage and the resulting currentStorage
variable.
- Around line 1328-1367: Add a unit test for getActionableNamedBackupRestores
that stubs assessNamedBackupRestore (used via the assess parameter) so some
calls reject with an error (e.g., simulate EBUSY) while others resolve to valid
assessment objects; call getActionableNamedBackupRestores with backups covering
both cases and a mocked currentStorage, then assert the returned assessments
only include the fulfilled results (i.e., rejected assessments are dropped) and
totalBackups equals the original backups length to verify the Promise.allSettled
handling and the filtering logic in getActionableNamedBackupRestores.

In `@test/codex-manager-cli.test.ts`:
- Around line 570-591: Add two deterministic vitest cases mirroring the existing
EBUSY test: (1) an EPERM case that should fall through to OAuth—create an Error
with code "EPERM", have
getActionableNamedBackupRestoresMock.mockRejectedValueOnce(epperm), set
selectMock.mockResolvedValueOnce("cancel"), call
runCodexMultiAuthCli(["auth","login"]) and assert exitCode === 0,
getActionableNamedBackupRestoresMock called once, confirmMock not called, and
createAuthorizationFlowMock called once (same behavior as the EBUSY test); (2) a
generic non-errno error case that must NOT be treated as the Windows lock
fallback—mock getActionableNamedBackupRestoresMock.mockRejectedValueOnce(new
Error("boom")) and assert the error propagates (or exitCode indicates failure)
and that createAuthorizationFlowMock is not invoked; locate tests around
runCodexMultiAuthCli and reference lib/codex-manager.ts:4383-4386 for the
errno-based fallback logic when adding these assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e5b6b9af-bcad-4291-8465-3112b22b7017

📥 Commits

Reviewing files that changed from the base of the PR and between e251c79 and 04491c4.

📒 Files selected for processing (13)
  • README.md
  • docs/README.md
  • docs/features.md
  • docs/getting-started.md
  • docs/index.md
  • docs/reference/commands.md
  • docs/reference/settings.md
  • docs/troubleshooting.md
  • lib/cli.ts
  • lib/codex-manager.ts
  • lib/storage.ts
  • test/codex-manager-cli.test.ts
  • test/recovery.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (3)
docs/**

⚙️ CodeRabbit configuration file

keep README, SECURITY, and docs consistent with actual CLI flags and workflows. whenever behavior changes, require updated upgrade notes and mention new npm scripts.

Files:

  • docs/troubleshooting.md
  • docs/index.md
  • docs/reference/commands.md
  • docs/reference/settings.md
  • docs/README.md
  • docs/getting-started.md
  • docs/features.md
lib/**

⚙️ CodeRabbit configuration file

focus on auth rotation, windows filesystem IO, and concurrency. verify every change cites affected tests (vitest) and that new queues handle EBUSY/429 scenarios. check for logging that leaks tokens or emails.

Files:

  • lib/cli.ts
  • lib/storage.ts
  • lib/codex-manager.ts
test/**

⚙️ CodeRabbit configuration file

tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior. reject changes that mock real secrets or skip assertions.

Files:

  • test/codex-manager-cli.test.ts
  • test/recovery.test.ts
🪛 LanguageTool
docs/getting-started.md

[style] ~77-~77: To make your writing flow more naturally, try moving the adverb ‘already’ closer to the verb ‘named’.
Context: ...rt Fresh Use the restore path when you already have named backup files and want to recover accoun...

(PERF_TENS_ADV_PLACEMENT)

🔇 Additional comments (19)
docs/features.md (2)

12-13: lgtm - capability entries align with shipped flows.

startup recovery prompt and backup restore manager match the interactive recovery path in lib/codex-manager.ts and the getActionableNamedBackupRestores export in lib/storage.ts.


59-60: lgtm - settings split and sync center entries are accurate.

matches the productized settings surface described in docs/reference/settings.md and the sync preview workflows.

lib/cli.ts (1)

258-260: lgtm - guard replacement is correct.

switching from direct isTTY() to isInteractiveLoginMenuAvailable() centralizes the gating logic and aligns with the exported helper used by lib/codex-manager.ts for interactive recovery prompts.

lib/storage.ts (1)

529-532: lgtm - clean interface for actionable recovery results.

the ActionableNamedBackupRecoveries interface properly separates actionable assessments from total backup count, which enables the ui to show "n of m backups recoverable" messaging.

docs/index.md (2)

15-16: lgtm - restore prompt note is accurate.

matches the interactive recovery flow gated by isInteractiveLoginMenuAvailable() in lib/cli.ts:29-31 and the backup detection in lib/codex-manager.ts.


44-48: lgtm - interactive workflow entries are clear.

these three workflows (backup restore, sync preview, settings split) align with the shipped dashboard ux documented in docs/reference/settings.md and docs/features.md.

docs/getting-started.md (3)

52-53: lgtm - restore prompt behavior documented accurately.

the description matches the interactive startup recovery flow: named backups in ~/.codex/multi-auth/backups/ with no active accounts triggers the prompt. this aligns with lib/codex-manager.ts interactive recovery logic.


75-85: lgtm - restore section paths are correct.

backup location ~/.codex/multi-auth/backups/<name>.json matches lib/storage.ts:44-45 (NAMED_BACKUP_DIRECTORY = "backups") and the path construction in lib/storage.ts:464-466.


87-95: lgtm - sync and settings section is clear.

the everyday vs advanced split and codex cli sync behavior description matches docs/reference/settings.md.

docs/README.md (2)

20-26: lgtm - portal descriptions updated consistently.

the user guide descriptions accurately reflect the new restore, sync, and settings content added to each referenced document.


40-41: lgtm - reference section descriptions updated.

commands.md and settings.md descriptions align with their updated content covering interactive entry points and the everyday/advanced split.

docs/troubleshooting.md (3)

21-23: lgtm - restore prompt behavior notes are accurate.

correctly describes: (1) interactive terminal gating via isInteractiveLoginMenuAvailable() in lib/cli.ts:29-31, (2) suppression after intentional reset flows, and (3) manual path via "restore from backup" menu option.


62-70: lgtm - backup restore troubleshooting table is helpful.

the symptom/cause/action entries correctly reference the backup location (~/.codex/multi-auth/backups/) and the filtering behavior from lib/storage.ts:1358-1364 (valid, !wouldExceedLimit, imported > 0).


83-93: lgtm - sync troubleshooting section covers key cases.

the one-way sync behavior, destination-only preservation, and backup rollback context descriptions align with the sync preview workflow documented in docs/reference/settings.md.

docs/reference/settings.md (3)

24-24: lgtm - everyday settings intro updated.

accurately reflects that the shipped menu defaults to everyday settings as the primary path.


83-88: lgtm - workflow notes clarify sync semantics.

important documentation: (1) sync is one-way not bidirectional, (2) target-only accounts are preserved. these match the sync preview behavior described in the pr objectives.


187-187: lgtm - storage backups recommendation added.

aligns with the rollback context described in the sync preview workflow and the backup/recovery flows added in this pr.

test/recovery.test.ts (1)

1224-1290: good actionable-backup filtering coverage

nice focused regression at test/recovery.test.ts:1224 validating getActionableNamedBackupRestores only returns actionable assessments and still counts total backups.

README.md (1)

103-107: workflow paths are aligned with current cli behavior

the restore and sync-center entry points described here match the actual flow in lib/codex-manager.ts:4304-4306 and lib/codex-manager.ts:4369-4404.

Also applies to: 120-127

Comment on lines +29 to +31
export function isInteractiveLoginMenuAvailable(): boolean {
return !isNonInteractiveMode() && isTTY();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

minor: redundant tty check.

isNonInteractiveMode() at lib/cli.ts:20 already returns true when !input.isTTY || !output.isTTY. the subsequent isTTY() call re-checks the same condition via lib/ui/ansi.ts:80-83.

this may be intentional defense-in-depth if input/output from node:process could differ from process.stdin/process.stdout in some edge runtime, but worth confirming. if redundant, consider simplifying to just !isNonInteractiveMode().

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/cli.ts` around lines 29 - 31, The isInteractiveLoginMenuAvailable
function redundantly calls isTTY(); simplify it by relying solely on
isNonInteractiveMode(): change isInteractiveLoginMenuAvailable to return
!isNonInteractiveMode() only. Update the function
(isInteractiveLoginMenuAvailable) and remove the extra isTTY() dependency so the
check is not duplicated (keep isNonInteractiveMode and isTTY implementations
unchanged).

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (2)
lib/codex-manager.ts (1)

4389-4392: ⚠️ Potential issue | 🟠 Major

narrow recovery fallback catch to windows lock errors only.

lib/codex-manager.ts:4389 still swallows every failure from backup assessment. this hides non-filesystem regressions and silently falls through to oauth. only eperm/ebusy should be treated as fallback; rethrow everything else.

proposed fix
-			} catch {
-				// Filesystem error (e.g. Windows EPERM/EBUSY) – skip recovery prompt
-				recoveryState = { assessments: [], totalBackups: 0 };
-			}
+			} catch (error) {
+				const code = (error as NodeJS.ErrnoException | undefined)?.code;
+				if (code === "EPERM" || code === "EBUSY") {
+					// windows filesystem lock edge case: continue with oauth
+					recoveryState = { assessments: [], totalBackups: 0 };
+				} else {
+					throw error;
+				}
+			}
#!/bin/bash
# confirm catch scope in login recovery path
rg -n -C3 'getActionableNamedBackupRestores|catch \(|EPERM|EBUSY|recoveryState = \{ assessments: \[\], totalBackups: 0 \}' lib/codex-manager.ts

# confirm vitest coverage for windows fallback and non-errno behavior
rg -n -C4 'getActionableNamedBackupRestores|EPERM|EBUSY|non-errno|throw|reject|recovery' test/codex-manager-cli.test.ts

expected result: tests should show eperm/ebusy fallthrough behavior and a non-errno case that does not get swallowed.

as per coding guidelines lib/**: focus on auth rotation, windows filesystem IO, and concurrency. verify every change cites affected tests (vitest) and that new queues handle EBUSY/429 scenarios. check for logging that leaks tokens or emails.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/codex-manager.ts` around lines 4389 - 4392, The catch block that
currently swallows all errors around backup assessment must be narrowed to only
handle Windows lock errors: change the bare catch to catch the error object
(e.g., catch (err)) and test err.code (or err.errno) for 'EPERM' or 'EBUSY'; if
it matches, set recoveryState = { assessments: [], totalBackups: 0 } as before,
otherwise rethrow the error so non-filesystem regressions surface. Locate this
change around the backup assessment / getActionableNamedBackupRestores call and
the existing recoveryState assignment to implement the conditional handling and
ensure other errors are not swallowed.
test/codex-manager-cli.test.ts (1)

570-591: ⚠️ Potential issue | 🟠 Major

cover both remaining backup-assessment error branches.

at test/codex-manager-cli.test.ts:570-591, this only locks in the ebusy branch from lib/codex-manager.ts:4383-4386. we still need deterministic regressions for eperm falling through to oauth on windows, and for a plain Error("boom") proving the fallback is errno-gated and does not swallow unrelated failures. without both, a broad catch can regress the login front door again. as per coding guidelines test/**: tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior. reject changes that mock real secrets or skip assertions.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/codex-manager/settings-hub.ts`:
- Around line 2697-2700: Add a vitest unit test that exercises promptSyncCenter
and verifies withQueuedRetry retries saveAccounts on transient errors: mock
saveAccounts (or saveAccountsMock used by the sync-center flow) to first reject
with a retryable error code (EBUSY or EPERM) and then resolve on the next call,
invoke promptSyncCenter to apply changes (so the code path at the call site
using withQueuedRetry(getStoragePath(), () => saveAccounts(syncedStorage))
runs), and assert saveAccounts was called at least twice and the final outcome
succeeds; ensure the mock resets afterward and use the same helper
utilities/stubs used by existing tests for dashboard/plugin flows to mirror
their retry coverage pattern.

In `@test/codex-manager-cli.test.ts`:
- Around line 1383-1464: The test currently only verifies the menu flow but
doesn't assert the actual backup restore side-effect; update the test to assert
that restoreNamedBackupMock was invoked and/or that the in-memory storageState
was updated after runCodexMultiAuthCli(["auth","login"]) completes (e.g.,
expect(restoreNamedBackupMock).toHaveBeenCalled() and
expect(storageState?.accounts?.[0]?.email).toBe("restored@example.com")); locate
the assertions around runCodexMultiAuthCli in the "offers backup recovery..."
test and add these checks to ensure the restore path executed end-to-end and not
just the UI prompts.

In `@test/recovery.test.ts`:
- Around line 328-334: Replace vi.restoreAllMocks() in the afterEach block with
vi.resetAllMocks() so mocked implementations (e.g.,
mockedReadParts.mockReturnValue(...) set in tests at lines referenced) are reset
between tests; keep the beforeEach vi.clearAllMocks() if you want to preserve
call-history clearing, but ensure vi.resetAllMocks() runs in afterEach to
restore mock implementations to their original state.

---

Duplicate comments:
In `@lib/codex-manager.ts`:
- Around line 4389-4392: The catch block that currently swallows all errors
around backup assessment must be narrowed to only handle Windows lock errors:
change the bare catch to catch the error object (e.g., catch (err)) and test
err.code (or err.errno) for 'EPERM' or 'EBUSY'; if it matches, set recoveryState
= { assessments: [], totalBackups: 0 } as before, otherwise rethrow the error so
non-filesystem regressions surface. Locate this change around the backup
assessment / getActionableNamedBackupRestores call and the existing
recoveryState assignment to implement the conditional handling and ensure other
errors are not swallowed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4f623c18-d231-4cfc-b850-48f96ba22f5a

📥 Commits

Reviewing files that changed from the base of the PR and between 04491c4 and 2177316.

📒 Files selected for processing (5)
  • lib/codex-manager.ts
  • lib/codex-manager/settings-hub.ts
  • lib/storage.ts
  • test/codex-manager-cli.test.ts
  • test/recovery.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (2)
test/**

⚙️ CodeRabbit configuration file

tests must stay deterministic and use vitest. demand regression cases that reproduce concurrency bugs, token refresh races, and windows filesystem behavior. reject changes that mock real secrets or skip assertions.

Files:

  • test/recovery.test.ts
  • test/codex-manager-cli.test.ts
lib/**

⚙️ CodeRabbit configuration file

focus on auth rotation, windows filesystem IO, and concurrency. verify every change cites affected tests (vitest) and that new queues handle EBUSY/429 scenarios. check for logging that leaks tokens or emails.

Files:

  • lib/storage.ts
  • lib/codex-manager.ts
  • lib/codex-manager/settings-hub.ts
🪛 ast-grep (0.41.1)
lib/codex-manager.ts

[warning] 725-728: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(
${String.fromCharCode(0x1b)}\\[[0-9;]*m|[${String.fromCharCode(0x00)}-${String.fromCharCode(0x1f)}${String.fromCharCode(0x7f)}],
"g",
)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

🔇 Additional comments (6)
lib/storage.ts (2)

529-532: clean public contract for actionable recovery state.

the exported shape in lib/storage.ts:529 is clear and matches the startup recovery flow expectations.


1328-1379: good windows-resilient assessment fan-out and filtering.

lib/storage.ts:1345 uses promise.allsettled correctly, and lib/storage.ts:1360 safely drops rejected assessments so one locked/unreadable backup does not block the rest.

lib/codex-manager.ts (4)

26-27: imports align with the shipped startup recovery flow.

the added imports at lib/codex-manager.ts:26 and lib/codex-manager.ts:80 cleanly wire interactive gating and actionable backup assessment into login.

Also applies to: 80-81


725-729: good terminal-safety hardening for displayed labels.

the sanitizer regex in lib/codex-manager.ts:725 is a static pattern and is appropriate for stripping ansi/control chars from user-visible text.


4187-4188: recovery suppression state is correctly threaded through destructive paths.

the flags added at lib/codex-manager.ts:4187 and set in lib/codex-manager.ts:4315 / lib/codex-manager.ts:4330 prevent prompting right after explicit reset/fresh actions, which keeps the flow predictable.

Also applies to: 4315-4315, 4330-4330


4399-4406: backup name sanitization before prompt rendering is solid.

lib/codex-manager.ts:4401 strips control sequences before building backupLabel, which reduces terminal injection risk in the recovery confirm text.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants